import { interval, Subject, Subscription } from "rxjs";

/**
 * websocket消息
 */
export interface WebsocketMessage {
  /**数据 */
  data: any;
  /**消息类型 */
  type: MessageType;
  /**错误信息 */
  error?: string;
}

export interface MessageType {

}

export class WebsocketService {
  /**服务器地址 */
  private url!: string;
  /**连接实例 */
  private socket!: WebSocket;
  /**消息事件 */
  subject: Subject<WebsocketMessage>;
  /** 二进制消息数据 */
  binary: Subject<Blob>;

  /**错误事件 */
  error: Subject<Event>;
  /**连接成功事件 */
  connected: Subject<Event>;
  /** 连接成功标记 */
  is_connected = false;
  /**连接检查周期 10分钟*/
  period = 60 * 1000 * 10;
  /** 定时检测连接对象 */
  serverTimeoutSubscription!: Subscription;
  /**重连标记 */
  reconnect_flag = false;
  /** 重连失败时的重试周期 5秒 */
  reconnect_period = 5 * 1000;
  /** 重连订阅对象*/
  reconnectSubscription!: Subscription;
  /** 记录运行连接subscription */
  runTimeSubscription!: Subscription;
  /** 记录运行连接时间 */
  runTimePeriod = 60 * 10000;
  noReconnect = false;
  constructor() {
    this.subject = new Subject();
    this.error = new Subject();
    this.connected = new Subject();
    this.binary = new Subject();
    // this.heartStart();
  }
  /**
   * 创建新连接
   * @param url 连接地址
   */
  connect(url: string) {
    if (!!url) this.url = url;
    this.create();
  }

  /**
   * 重新连接
   *
   */
  private reconnect() {
    if (this.is_connected) {
      this.stopReconnect();
      console.log('已经连接成功,停止重连');
      return;
    }
    // 如果正在连接中,则直接return,避免产生多个轮训事件
    if (this.reconnect_flag) {
      console.log('正在重连,直接返回');
      return;
    }
    // 开始重连
    this.reconnect_flag = true;
    // 如果没能成功连接,则定时重连
    this.reconnectSubscription = interval(this.reconnect_period).subscribe(async val => {
      console.log(`重连:${val}次`);
      const url = this.url;
      // 重新连接
      this.connect(url);
    });
  }
  /**
   * 停止重连
   */
  private stopReconnect() {
    // 连接标识置为false
    this.reconnect_flag = false;
    // 取消订阅
    if (typeof this.reconnectSubscription !== 'undefined' && this.reconnectSubscription != null) {
      this.reconnectSubscription.unsubscribe();
    }
  }

  /**
   * 开始心跳检测
   */
  private heartStart() {
    this.serverTimeoutSubscription = interval(this.period).subscribe(val => {
      if (this.socket == null || this.socket.readyState != 1) {
        this.heartStop();
        this.reconnect();
      }
    });
  }
  /**
   * 停止心跳检测
   */
  private heartStop() {
    if (
      typeof this.serverTimeoutSubscription !== 'undefined' &&
      this.serverTimeoutSubscription != null
    ) {
      this.serverTimeoutSubscription.unsubscribe();
    }
  }
  /**
   * 开始计算运行时间
   */
  private calcRunTime() {
    this.runTimeSubscription = interval(this.runTimePeriod).subscribe(period => {
      console.log('运行时间', `${period}分钟`);
    });
  }

  /**
   * 停止计算运行时间
   */
  private stopRunTime() {
    if (typeof this.runTimeSubscription !== 'undefined' && this.runTimeSubscription !== null) {
      this.runTimeSubscription.unsubscribe();
    }
  }
  /**
   * 创建连接
   */
  private create() {
    this.socket = new WebSocket(this.url);
    this.socket.onopen = e => this.onopen(e);
    this.socket.onmessage = e => this.onmessage(e);
    this.socket.onclose = e => this.onclose(e);
    this.socket.onerror = e => this.onerror(e);
  }
  /**
   * 连接打开
   * @param event 打开事件
   */
  private onopen(event: Event) {
    // 设置连接成功
    this.is_connected = true;
    this.connected.next(event);
    // 如果是重连中
    if (this.reconnect_flag) {
      // 1.停止重连
      this.stopReconnect();
      // 2.重新开启心跳
      this.heartStart();
      // 3.重新开始计算运行时间
      this.calcRunTime();
    }
  }
  /**
   * 接收消息
   * @param event 接收消息事件
   */
  private onmessage(event: any) {
    if (event.data instanceof Blob) {
      this.binary.next(event.data);
    } else {
      const message = JSON.parse(event.data);
      this.subject.next(message);
    }
  }
  /**
   * 连接关闭
   * @param event
   */
  private onclose(event: Event) {
    this.is_connected = false;
    this.socket.close();
    // 关闭时开始重连
    if (this.noReconnect == false) {
      this.reconnect();
      this.stopRunTime();
    }
  }
  /**
   * 连接异常
   * @param event
   */
  public onerror(event: Event) {
    console.log('连接异常', event);
    this.is_connected = false;
    this.error.next(event);
  }
  /**
   * 发送消息
   * @param message
   */
  public send(message: WebsocketMessage) {
    this.socket.send(JSON.stringify(message));
  }

  public close() {
    this.noReconnect = true;
    this.socket?.close();
  }
  public noReconnectWs() {
    this.stopReconnect();
  }
}